<template>
	<view class="cameraBox">
		<view class="cameraBox-title" :class="colorState?'activeColor1':'activeColor2'">{{verifyText}}</view>
		<view class="cameraBox-container" :style="{background: bgColor}">
			<!-- <image src="https://fire-system.oss-cn-beijing.aliyuncs.com/web/fire-technology/camera-frame.png" mode="">
			</image> -->
			<view class="cameraBox-container-img">
				<camera class="cameraBox-container-camera" device-position="front" resolution="high" flash="off"
					@stop="stopCamera" @error="errorCamera"></camera>
			</view>

		</view>
		<canvas id="myCanvas" canvas-id="myCanvas"
			:style="'width:'+screenWidth+'px;'+'height:'+screenHeight+'px'"></canvas>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				colorState: false,
				bgColor: '#fff', // 北京颜色
				screenWidth: 375, // canvas width
				screenHeight: 640, // canvas height
				base64Image: "", // 面容图片地址
				listener: null, // 获取 Camera 实时帧数据。
				videoCtx: null, // 创建并返回 camera 组件的上下文 cameraContext 对象。
				VKSession: null, // 检测人脸并返回数据
				faceVerifyTime: null, //面容验证倒计时
				count: 0, // 倒计时视频帧
				verifyText: "请移动面容到框内", // 人脸识别提示语
				isCentre: false, // 是否面容在正中间
				startVerify: false, // 是否正在验证
				frameData: null, // 相机帧
				isFlag2: true, // 动态设置canvas宽和高
				// 人脸返回的数据
				face: {
					// 人脸框的左上角(x,y)坐标
					origin: {
						x: 0,
						y: 0
					},
					// 人脸框的宽和高(w,h)
					size: {
						width: 0,
						height: 0
					},
					points: [], // 106点在图像中的(x,y)坐标
					angle: {}, // 人脸角度信息(pitch, yaw, roll)
					confidence: {}, // 人脸关键点的置信度
				},
				setIntColor: null, // 颜色定时器
				paramsList: {}, // 传递的参数
				correctStata: false, // 是否开始截取帧
				frameDataList: [], // 所有面容
				frameDataListSure: [], // 要提交的面容
				base64ImageList: [], // bse64图片集
			}
		},
		onLoad(e) {
			// console.log('前边传过来的数据', e)
			this.getCameraPurview(); // 获取相机权限---检测是否获取权限
			this.paramsList = JSON.parse(e.paramsList)
		},
		onUnload() {
			// 监听页面卸载
			this.VKSession.destroy(); // 销毁相机
		},
		methods: {
			// 获取相机权限---检测是否获取权限
			getCameraPurview() {
				uni.getSetting({
					success: (res) => {
						if (res.authSetting['scope.camera'] === true) {
							this.initFaceVerify() // 打开相机，开始人脸检测
						} else if (res.authSetting['scope.camera'] === false) {
							this.getCameraAuth() // 获取相机权限---进行授权
						} else {
							this.initFaceVerify() // 打开相机，开始人脸检测
						}
					}
				})
			},
			// 获取相机权限---进行授权
			getCameraAuth() {
				uni.showModal({
					title: '温馨提示',
					content: '需要获取您摄像头权限才能更好的为您服务！是否授权摄像头权限？',
					confirmText: '授权',
					confirmColor: '#f94218',
					success: (res) => {
						if (res.confirm) {
							// 选择弹框内授权
							uni.openSetting({
								success: (res) => {
									if (res.authSetting['scope.camera']) {
										this.initFaceVerify() // 打开相机，开始人脸检测
									} else {
										this.verifyText = "您未授权摄像头权限"
										this.cameraError() // 获取相机失败或者人脸核验失败
									}
								}
							})
						} else if (res.cancel) {
							this.verifyText = "您未授权摄像头权限"
							this.cameraError() // 获取相机失败或者人脸核验失败
						}
					}
				})
			},
			// 初始化相机引擎，开始人脸识别
			initFaceVerify() {
				// 创建并返回 camera 组件的上下文 cameraContext 对象。
				this.videoCtx = uni.createCameraContext();
				// 获取 Camera 实时帧数据。
				this.listener = this.videoCtx.onCameraFrame(frame => {
					console.log('当前视频流---', frame)
					this.count++;
					if (this.count === 5) {
						this.detectFace(frame);
						this.count = 0;
						this.frameData = frame
						if (this.correctStata) {
							this.frameDataList.push(frame)
						} else {
							this.frameDataList = []
						}
					}
					// 动态设置canvas的宽高, 不设置会导致部分机型人脸不完整导致不能识别-----很重要!很重要!很重要!
					if (this.isFlag2) {
						this.isFlag2 = false;
						this.screenWidth = frame.width;
						this.screenHeight = frame.height;
					}
				});
				// 检测人脸并返回数据
				this.VKSession = wx.createVKSession({
					version: 'v1',
					track: {
						plane: {
							mode: 1
						},
						face: {
							mode: 2
						}
					}
				});
				this.VKSession.on('updateAnchors', (anchors) => {
					// console.log(anchors, '有面容')
					// console.log('106点在图像中的(x,y)坐标的中心坐标', anchors[0].points[43])
					// console.log('人脸框的左上角(x,y)坐标', anchors[0].origin)
					// console.log('人脸框的宽和高(w,h)', anchors[0].size)
					// console.log('人脸角度信息(pitch仰俯角（点头）, yaw偏航角（摇头）, roll翻滚角（左右倾）,得分)', anchors[0].angle)
					// console.log('人脸关键点的置信度', anchors[0].confidence)
					if (this.startVerify) {
						return;
					}
					this.anchorFace = anchors[0];
					this.face.points = this.anchorFace.points // 106点在图像中的(x,y)坐标
					this.face.origin = this.anchorFace.origin // 人脸框的左上角(x,y)坐标
					this.face.size = this.anchorFace.size // 人脸框的宽和高(w,h)
					this.face.angle = this.anchorFace.angle // 人脸角度信息(pitch仰俯角（点头）, yaw偏航角（摇头）, roll翻滚角（左右倾）,得分)'
					this.face.confidence = this.anchorFace.confidence // 人脸关键点的置信度
					this.isFaceCentered() // 面容逻辑判断
				})
				// 面容消失，人脸移除相机
				this.VKSession.on('removeAnchors', () => {
					if (this.startVerify) {
						return;
					}
					this.correctStata = false
					this.frameDataList = []
					this.verifyText = '请移动面容到框内'
					this.isCentre = false
					this.face = {}
					clearTimeout(this.faceVerifyTime)
					this.faceVerifyTime = null;
					this.stopChangeColor() // 停止切换颜色
				})
				setTimeout(() => {
					// 直接开始
					this.handleStart() // 相机推流开始
				}, 500);
			},
			// 相机拍照
			picture() {
				this.startVerify = true
				clearTimeout(this.faceVerifyTime)
				this.faceVerifyTime = null;
				this.videoCtx.takePhoto({
					quality: 'high',
					success: (e) => {
						//上传照片接口，图片换成远端url地址 自行替换
						// console.log('paizhao', e)
					}
				})
			},
			// 相机开始推流
			handleStart() {
				this.VKSession.start((errno) => {
					console.warn('VKSession.start errno', errno);
				});
				this.listener.start();
			},
			// 相机停止推流
			handleStop() {
				this.listener.stop({
					complete: (res) => {
						console.warn('listener.stop', res);
					}
				});
				this.VKSession.stop();
			},
			// 获取面容
			async detectFace(frame) {
				// 获取面容
				this.VKSession.detectFace({
					frameBuffer: frame.data, // 图片 ArrayBuffer 数据。人脸图像像素点数据，每四项表示一个像素点的 RGBA
					width: frame.width, // 图像宽度
					height: frame.height, // 图像高度
					scoreThreshold: 0.9, // 评分阈值
					sourceType: 0,
					modelMode: 2
				});
			},
			// 判断面容逻辑
			isFaceCentered() {
				// 判断面容是否在中间
				if (!this.face.points) {
					return;
				}
				let points = this.face.points[50]; //位置
				let size = this.face.size; //大小
				let {
					pitch,
					roll,
					yaw
				} = this.face.angle;
				let standard = 0.3;
				let centerWidth = 150;
				let centerHeight = 150;
				// console.log('size', size)
				// console.log('points', points)
				// console.log('this.face.points', this.face.points)
				if (points.x > 0.65 || points.x < 0.4 || points.y > 0.65 || points.y < 0.35) {
					// console.log('请移动面容到框内', points.x, points.y)
					this.verifyTex = '请移动面容到框内'
					this.isCentre = false
					this.face = {}
					clearTimeout(this.faceVerifyTime)
					this.faceVerifyTime = null;
					return
				} else if (size.width > 0.95 || size.width < 0.32) {
					// console.log('请移动面容显示完整面容')
					this.verifyText = '请移动面容显示完整面容'
					this.isCentre = false
					this.face = {}
					clearTimeout(this.faceVerifyTime)
					this.faceVerifyTime = null;
					return
				} else if (Math.abs(pitch) >= standard || Math.abs(roll) >= standard || Math
					.abs(yaw) >= standard) {
					this.verifyText = '请平视摄像头';
				} else if (
					points.x < (this.frameData.width - centerWidth) / 2 ||
					points.x > (this.frameData.width - centerWidth) / 2 + centerWidth ||
					points.y < (this.frameData.height - centerHeight) / 2 ||
					points.y > (this.frameData.height - centerHeight) / 2 + centerHeight
				) {
					this.verifyText = '请将人脸对准中心位置';
				}
				this.verifyText = '请保持不动'
				this.isCentre = true // 人脸居中
				this.correctStata = true
				if (this.frameDataList.length === 8) {
					this.frameDataListSure = this.getRandomElements(this.frameDataList, 4) // 数组中随机获取5组数据
					if (this.frameDataListSure.length > 0) {
						this.verifyCentre() // 开始验证面容
						return
					}
				}
			},
			// 数组中随机获取5组数据
			getRandomElements(arr, count) {
				const result = new Array(count);
				const copy = arr.slice();
				for (let i = 0; i < count; i++) {
					const index = Math.floor(Math.random() * copy.length);
					result[i] = copy[index];
					copy.splice(index, 1);
				}
				return result;
			},

			// 开始验证面容
			verifyCentre() {
				// 开始验证面容
				if (this.faceVerifyTime || this.startVerify) {
					return
				} else {
					this.changeColor() // 背景色闪烁
					this.faceVerifyTime = setTimeout(async () => {
						console.log('this.isCentre', this.isCentre)
						// 是否面容在正中间
						if (this.isCentre) {
							// this.handleStop() // 停止相机推流
							const base64One = await this.changeDataToBase64(this.frameDataListSure[0])
							const base64Two = await this.changeDataToBase64(this.frameDataListSure[1])
							const base64Three = await this.changeDataToBase64(this.frameDataListSure[2])
							const base64Four = await this.changeDataToBase64(this.frameDataListSure[3])
							Promise.allSettled([base64One, base64Two, base64Three, base64Four]).then(arr => {
								console.log('allSettled', arr) // 全部执行完才会输出
								// uni.setStorageSync('imageBase64List', '');
								// uni.setStorageSync('imageBase64List', arr);
								this.$pageJump.reLaunch(
									'/pages/maintenance/mechanism/home/components/mechanismWork/mechanismWorking?paramsList=' +
									JSON.stringify(this.paramsList) + '&imageBase64List=' +
									encodeURIComponent(JSON.stringify(arr))
								)
							})

						} else {
							clearTimeout(this.faceVerifyTime)
							this.faceVerifyTime = null;
						}
					}, 500);
				}
			},
			// android or ios获取视频帧
			changeDataToBase64(frame) {
				console.log('frame', frame)
				return new Promise((resolve, reject) => {
					let data = new Uint8Array(frame.data);
					let clamped = new Uint8ClampedArray(data);
					let width = this.screenWidth;
					let height = frame.height * this.screenWidth / frame.width;
					uni.canvasPutImageData({
						canvasId: 'myCanvas',
						x: 0,
						y: 0,
						width: frame.width,
						height: frame.height,
						data: clamped,
						success() { // 转换临时文件
							uni.canvasToTempFilePath({
								x: 0,
								y: 0,
								width: width,
								height: height,
								canvasId: 'myCanvas',
								fileType: 'jpg',
								destWidth: width,
								destHeight: height,
								quality: 1, // 精度修改
								success(res) {
									// 临时文件转base64
									uni.getFileSystemManager().readFile({
										filePath: res.tempFilePath, //选择图片返回的相对路径 
										encoding: 'base64', //编码格式  
										success: res => {
											// 保存base64
											// setTimeout(() => {
											// 	that.stopChangeColor() // 停止切换颜色
											// }, 1000)
											resolve(res.data);
										}
									})
								},
								fail() {
									reject(false);
								}
							});
						},
						fail() {
							// console.log(error);
						}
					})
				})
			},
			// 相机发生错误
			errorCamera() {
				this.verifyText = '相机异常'
				this.cameraError() // 人脸核验失败
			},
			// 相机停止
			stopCamera() {
				this.verifyText = '相机异常'
				this.cameraError() // 人脸核验失败
			},
			// 人脸核验失败
			cameraError() {
				let that = this
				this.stopChangeColor() // 停止切换颜色
				that.t = setTimeout(() => {
					that.$pageJump.reLaunch(
						'/pages/maintenance/mechanism/home/components/mechanismWork/mechanismWorking?maintenanceId=' +
						that.paramsList.maintenanceId
					)
					clearTimeout(that.t)
				}, 1000);
			},
			// 切换背景色
			changeColor() {
				let colorList = ['red', 'blue', 'yellow', 'red', 'blue', 'yellow'];
				let colorIndex = 0;
				this.setIntColor = setInterval(() => {
					colorIndex = (colorIndex + 1) % colorList.length; // 取余
					this.bgColor = colorList[colorIndex]
					this.colorState = true
				}, 500); // 每800毫秒（1秒）改变一次颜色
			},
			// 停止切换颜色
			stopChangeColor() {
				// console.log('停止切换颜色', this.setIntColor)
				clearInterval(this.setIntColor)
				this.setIntColor = null
				this.bgColor = '#fff'
				this.colorState = false
			}
		}
	}
</script>

<style scoped lang="scss">
	.cameraBox {
		position: relative;

		&-title {
			position: absolute;
			width: 100%;
			text-align: center;
			padding-top: 100rpx;
			font-size: 36rpx;
			color: #333;
			z-index: 999;
		}

		&-container {
			height: 100vh;
			position: relative;
			padding-top: 90rpx;

			&-img {
				background: url('https://fire-system.oss-cn-beijing.aliyuncs.com/web/fire-technology/camera-frame.png') no-repeat;
				background-size: cover;
				width: 100%;
				height: 850rpx;
				padding-top: 207rpx;
			}

			&-camera {
				width: 440rpx;
				height: 440rpx;
				border-radius: 50% 50%;
				margin: 0 auto;
			}
		}

		#myCanvas {
			position: absolute;
			z-index: 1;
			top: -10000rpx;
		}
	}

	.activeColor1 {
		color: #fff;
	}

	.activeColor2 {
		color: #333 !important;
	}
</style>